-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

-- Description: 装备测试入口
local class = require 'mc.class'
local utils = require 'mc.utils'
local log = require 'mc.logging'
local ipmi_req = require 'general_hardware.ipmi.ipmi'
local hw_channel = require 'hw_channel_test'
local skynet = require 'skynet'
local jtag_test = require 'jtag_test'
local eye_diagrame = require 'eye_diagram'
local vrd_upgrade_sim = require 'vrd_upgrade_sim'
local drv = require 'libsoc_adapter.jtag'
local ctx = require 'mc.context'
local client = require 'client'

local manufacture_app = class()

local HOST_TYPE<const> = '/bmc/kepler/Managers/1/Multihost'
local STATUS_COMPLETE<const> = "Complete"
local STATUS_TESTING<const> = "Testing"
local STATUS_UNSTART<const> = "Unstart"

local RESULT_SUCCEED<const> = "Succeed"
local RESULT_FAILED<const> = "Failed"
local RESULT_NON<const> = "Non"

local POWERON<const> = 1
local POWEROFF<const> = 0

local ERR<const> = 1
local OK<const> = 0

function manufacture_app.new(app)
    return setmetatable({app = app}, manufacture_app)
end

function manufacture_app:register_ipmi()
    self.app:register_ipmi_cmd(ipmi_req.SetCpldChannel, jtag_test.set_cpld_channel)
    self.app:register_ipmi_cmd(ipmi_req.GetCpldDeviceidInfo, jtag_test.get_cpld_deviceid_info)
    self.app:register_ipmi_cmd(ipmi_req.GetEyeDiagram, eye_diagrame.get_eye_diagrame_result)
    self.app:register_ipmi_cmd(ipmi_req.GetPCIeEyeDiagram, eye_diagrame.get_pcie_eye_diagrame)
end

function manufacture_app:init()
    self.volt_res = {}
    self.hw_result = {}
    self.hw_description = {}
    self.card_presence_res = RESULT_NON
    self:register_rpc_methods()
    self:register_ipmi()
    self.app:set_dft_mode()
    self.vrd_upgrade_sim = vrd_upgrade_sim
    vrd_upgrade_sim:init()
end

local function is_multihost_type()
    local objs = client:GetMultihostObjects()
    for path, obj in pairs(objs) do
        if path == HOST_TYPE then
            return obj.HostType == 'Multihost'
        end
    end
    return false
end

-- 校验电压 上电：位于标准值+-迟滞值 下电：小于标准值+迟滞值
local function check_volt(obj)
    local real = obj.VoltValue
    if not real or not obj.PowOnStandValue or
        not obj.PowOffStandValue or not obj.HystValue then
        return false
    end
    log:debug("vlot test: real - %s  powerON_stand - %s  powerOff_stand - %s hyst - %s",
        real, obj.PowOnStandValue, obj.PowOffStandValue, obj.HystValue)
    if obj.DevPowerStatus == POWERON then
        if real < obj.PowOnStandValue + obj.HystValue and
        real > obj.PowOnStandValue - obj.HystValue then
            return true
        end
    elseif obj.DevPowerStatus == POWEROFF then
        if real < obj.PowOffStandValue + obj.HystValue then
            return true
        end
    end
    return false
end

local function check_memory_volt(obj)
    local vlot = obj.VoltValue
    if not vlot or not obj.LowThreshold or
        not obj.HighThreshold or not obj.DevPowerStatus then
        return false
    end
    log:debug("memroy vlot test: real - %f , low - %f , hig - %f",
        vlot, obj.LowThreshold, obj.HighThreshold)
    if obj.DevPowerStatus == POWERON then
        if vlot > obj.HighThreshold or vlot < obj.LowThreshold then
            return false
        end
    end
    return true
end

-- 检查板卡在位是否符合预期值
local function check_card_presence(obj)
    local presence = obj.Presence
    if not obj.ExpectVal or not presence then
        return false
    end
    return presence == obj.ExpectVal
end

function manufacture_app:get_private_prop(obj)
    for _, v in pairs(self.app.dft_list) do
        if v.Type == obj.Type and v.Id == obj.Id and
            v.Slot == obj.Slot and v.DeviceNum == obj.DeviceNum then
            return v
        end
    end
    return nil
end

function manufacture_app:register_cpld_test()
    self.app:ImplDftCpldManufactureStart(function (obj, ...)
        return self:DftCpldStart(obj)
    end)
    self.app:ImplDftCpldManufactureStop(function (obj, ...)
        return self:DftCpldStop(obj)
    end)
    self.app:ImplDftCpldManufactureGetResult(function (obj, ...)
        local private_obj = self:get_private_prop(obj)
        return self:DftCpldGetResult(obj, private_obj)
    end)
end

function manufacture_app:register_io_test()
    self.app:ImplDftIOTestManufactureStart(function (obj, ...)
        local private_obj = self:get_private_prop(obj)
        return self:DftIOStart(obj, private_obj)
    end)
    self.app:ImplDftIOTestManufactureStop(function (obj, ...)
        return self:DftIOStop(obj)
    end)
    self.app:ImplDftIOTestManufactureGetResult(function (obj, ...)
        return self:DftIOGetResult(obj)
    end)
end

function manufacture_app:register_button_cell_volt_test()
    self.app:ImplDftButtonCellManufactureStart(function (obj, ...)
        local private_obj = self:get_private_prop(obj)
        return self:VoltStart(obj, private_obj)
    end)
    self.app:ImplDftButtonCellManufactureStop(function (obj, ...)
        return self:VoltStop(obj)
    end)
    self.app:ImplDftButtonCellManufactureGetResult(function (obj, ...)
        return self:VoltGetResult(obj)
    end)
end

function manufacture_app:register_hw_channel_test()
    self.app:ImplDftHwChannelManufactureStart(function (obj, ...)
        local private_obj = self:get_private_prop(obj)
        return self:DftHwChannelStart(private_obj)
    end)
    self.app:ImplDftHwChannelManufactureStop(function (obj, ...)
        return self:DftHwChannelStop(obj)
    end)
    self.app:ImplDftHwChannelManufactureGetResult(function (obj, ...)
        return self:DftHwChannelGetResult(obj)
    end)
end

function manufacture_app:register_card_presence()
    self.app:ImplDftCardPresenceManufactureStart(function (obj, ...)
        local private_obj = self:get_private_prop(obj)
        return self:DftCardPresenceStart(obj, private_obj)
    end)
    self.app:ImplDftCardPresenceManufactureStop(function (obj, ...)
        return self:DftCardPresenceStop(obj)
    end)
    self.app:ImplDftCardPresenceManufactureGetResult(function (obj, ...)
        return self:DftCardPresenceGetResult(obj)
    end)
end

function manufacture_app:register_flash_test()
    self.app:ImplDftFlashManufactureStart(function (obj, ...)
        return self:DftStart(obj)
    end)
    self.app:ImplDftFlashManufactureStop(function (obj, ...)
        return self:DftStop(obj)
    end)
    self.app:ImplDftFlashManufactureGetResult(function (obj, ...)
        return self:DftGetResult(obj)
    end)
end

local function set_jtag_accessor(obj)
    obj.SwitchAccessor = obj.SwitchData
end
 
function manufacture_app:DftJtagStart(obj, private_obj)
    obj.Status = STATUS_TESTING
    local index = string.format("%d_%d_%d", obj.Id, obj.Slot, obj.DeviceNum)
    self.hw_result[index] = RESULT_NON
 
    -- 1.配置通路
    local ok = pcall(set_jtag_accessor, private_obj)
    if not ok then
        log:error("dft_jtag: set accessor fail")
        self.hw_result[index] = RESULT_FAILED
        return
    end

    if is_multihost_type() then
        skynet.sleep(200)  ---HPCjtag链路经由L1板，链路较长，延时2s保证CPLD通道切换切到对应模组板
    end
    
    -- 2.检查bypass
    local input_chip = private_obj.InputChip
    local ok, ret = pcall(function()
        return input_chip:BypassChannelTest(ctx.new(), 0, private_obj.Channel)
    end)
    if not ok or not ret then
        log:error("dft_jtag: check bypass channel fail. ret[%s]", ret)
        self.hw_result[index] = RESULT_FAILED
        return
    end

    log:notice("dft_jtag: test succeeded")
    self.hw_result[index] = RESULT_SUCCEED
end
 
function manufacture_app:DftJtagStop(obj)
    local index = string.format("%d_%d_%d", obj.Id, obj.Slot, obj.DeviceNum)
    obj.Status = STATUS_UNSTART
    self.hw_result[index] = RESULT_NON
end
 
function manufacture_app:DftJtagGetResult(obj, private_obj)
    local index = string.format("%d_%d_%d", obj.Id, obj.Slot, obj.DeviceNum)
    obj.Status = STATUS_COMPLETE
    return self.hw_result[index], ""
end
 
function manufacture_app:register_jtag_test()
    self.app:ImplDftJTAGManufactureStart(function (obj, ...)
        local private_obj = self:get_private_prop(obj)
        return self:DftJtagStart(obj, private_obj)
    end)
    self.app:ImplDftJTAGManufactureStop(function (obj, ...)
        return self:DftJtagStop(obj)
    end)
    self.app:ImplDftJTAGManufactureGetResult(function (obj, ...)
        return self:DftJtagGetResult(obj)
    end)
end

function manufacture_app:register_memory_volt_test()
    self.app:ImplDftMemoryVoltManufactureStart(function (obj, ...)
        local private_obj = self:get_private_prop(obj)
        return self:MemoryVoltStart(obj, private_obj)
    end)
    self.app:ImplDftMemoryVoltManufactureStop(function (obj, ...)
        return self:MemoryVoltStop(obj)
    end)
    self.app:ImplDftMemoryVoltManufactureGetResult(function (obj, ...)
        return self:MemoryVoltGetResult(obj)
    end)
end

function manufacture_app:register_volt_test()
    self.app:ImplDft12V0VlotManufactureStart(function (obj, ...)
        local private_obj = self:get_private_prop(obj)
        return self:VoltStart(obj, private_obj)
    end)
    self.app:ImplDft12V0VlotManufactureStop(function (obj, ...)
        return self:VoltStop(obj)
    end)
    self.app:ImplDft12V0VlotManufactureGetResult(function (obj, ...)
        return self:VoltGetResult(obj)
    end)

    self.app:ImplDft5V0VlotManufactureStart(function (obj, ...)
        local private_obj = self:get_private_prop(obj)
        return self:VoltStart(obj, private_obj)
    end)
    self.app:ImplDft5V0VlotManufactureStop(function (obj, ...)
        return self:VoltStop(obj)
    end)
    self.app:ImplDft5V0VlotManufactureGetResult(function (obj, ...)
        return self:VoltGetResult(obj)
    end)

    self.app:ImplDft3V3VlotManufactureStart(function (obj, ...)
        local private_obj = self:get_private_prop(obj)
        return self:VoltStart(obj, private_obj)
    end)
    self.app:ImplDft3V3VlotManufactureStop(function (obj, ...)
        return self:VoltStop(obj)
    end)
    self.app:ImplDft3V3VlotManufactureGetResult(function (obj, ...)
        return self:VoltGetResult(obj)
    end)
end

function manufacture_app:register_rpc_methods()
    self:register_button_cell_volt_test()
    self:register_memory_volt_test()
    self:register_volt_test()
    self:register_card_presence()
    self:register_flash_test()
    self:register_jtag_test()
    self:register_cpld_test()
    self:register_io_test()
    self:register_hw_channel_test()
end

function manufacture_app:MemoryVoltStart(obj, private_obj)
    local index = string.format("%d_%d_%d", obj.Id, obj.Slot, obj.DeviceNum)
    self.volt_res[index] = RESULT_NON
    obj.Status = STATUS_TESTING
    if not private_obj then
        obj.Status = STATUS_COMPLETE
        self.volt_res[index] = RESULT_FAILED
        return
    end
    local ok = check_memory_volt(private_obj)
    if not ok then
        obj.Status = STATUS_COMPLETE
        self.volt_res[index] = RESULT_FAILED
        return
    end
    obj.Status = STATUS_COMPLETE
    self.volt_res[index] = RESULT_SUCCEED
end

function manufacture_app:MemoryVoltStop(obj)
    local index = string.format("%d_%d_%d", obj.Id, obj.Slot, obj.DeviceNum)
    obj.Status = STATUS_UNSTART
    self.volt_res[index] = RESULT_NON
end

function manufacture_app:MemoryVoltGetResult(obj)
    local index = string.format("%d_%d_%d", obj.Id, obj.Slot, obj.DeviceNum)
    return self.volt_res[index], ""
end

function manufacture_app:VoltStart(obj, private_obj)
    local index = string.format("%d_%d_%d", obj.Id, obj.Slot, obj.DeviceNum)
    self.volt_res[index] = RESULT_NON
    obj.Status = STATUS_TESTING
    if not private_obj then
        obj.Status = STATUS_COMPLETE
        self.volt_res[index] = RESULT_FAILED
        return
    end
    local ok = check_volt(private_obj)
    if not ok then
        obj.Status = STATUS_COMPLETE
        self.volt_res[index] = RESULT_FAILED
        return
    end
    obj.Status = STATUS_COMPLETE
    self.volt_res[index] = RESULT_SUCCEED
end

function manufacture_app:VoltStop(obj)
    local index = string.format("%d_%d_%d", obj.Id, obj.Slot, obj.DeviceNum)
    obj.Status = STATUS_UNSTART
    self.volt_res[index] = RESULT_NON
end

function manufacture_app:VoltGetResult(obj)
    local index = string.format("%d_%d_%d", obj.Id, obj.Slot, obj.DeviceNum)
    return self.volt_res[index], ""
end

function manufacture_app:DftCardPresenceStart(obj, private_obj)
    obj.Status = STATUS_TESTING
    if not private_obj then
        obj.Status = STATUS_COMPLETE
        self.card_presence_res = RESULT_FAILED
        return
    end
    local ok = check_card_presence(private_obj)
    if not ok then
        obj.Status = STATUS_COMPLETE
        self.card_presence_res = RESULT_FAILED
        return
    end
    obj.Status = STATUS_COMPLETE
    self.card_presence_res = RESULT_SUCCEED
end

function manufacture_app:DftCardPresenceStop(obj)
    obj.Status = STATUS_UNSTART
    self.card_presence_res = RESULT_NON
end

function manufacture_app:DftCardPresenceGetResult(obj)
    return self.card_presence_res, ""
end

function manufacture_app:DftStart(obj)
    obj.Status = STATUS_TESTING
end

function manufacture_app:DftFlashStart(obj)
    obj.Status = STATUS_TESTING
end

function manufacture_app:DftStop(obj)
    obj.Status = STATUS_UNSTART
end

function manufacture_app:DftGetResult(obj)
    obj.Status = STATUS_COMPLETE
    return RESULT_SUCCEED, ""
end

function manufacture_app:DftCpldStart(obj)
    obj.Status = STATUS_TESTING
end

function manufacture_app:DftCpldStop(obj)
    obj.Status = STATUS_UNSTART
end

function manufacture_app:DftCpldGetResult(obj, private_obj)
    obj.Status = STATUS_COMPLETE
    if private_obj.CpldStatus ~= 0 then
        return RESULT_FAILED, ""
    end
    return RESULT_SUCCEED, ""
end

local function set_accessor(obj)
    obj.Destination = obj.Data
end

local function get_accessor(obj)
    obj.Data = obj.Destination
end

local function get_and_compare_accessor(obj)
    return obj.Destination == obj.Data
end

local WRITE_ACTION<const> = 0
local READ_ACTION<const> = 1
local KEEP_ACTION<const> = 2

local io_res = 0
function manufacture_app:DftIOStart(obj, private_obj)
    obj.Status = STATUS_TESTING
    local type = private_obj.ActionType
    if type == WRITE_ACTION then
        local ok = pcall(set_accessor, private_obj)
        if not ok then
            io_res = 1
            return
        end
    elseif type == READ_ACTION then
        local ok, res = pcall(get_and_compare_accessor, private_obj)
        if not ok then
            io_res = 1
            return
        end
        io_res = res == true and 0 or 1
    elseif type == KEEP_ACTION then
        local ok = pcall(get_accessor, private_obj)
        if not ok then
            io_res = 1
            return
        end
    end
    obj.Status = STATUS_COMPLETE
end

function manufacture_app:DftIOStop(obj)
    obj.Status = STATUS_UNSTART
end

function manufacture_app:DftIOGetResult(obj, private_obj)
    obj.Status = STATUS_COMPLETE
    if io_res ~= 0 then
        return RESULT_FAILED, RESULT_FAILED
    end
    return RESULT_SUCCEED, ""
end

function manufacture_app:DftHwChannelStart(obj)
    local index = string.format("%d_%d_%d", obj.Id, obj.Slot, obj.DeviceNum)
    self.hw_result[index] = RESULT_NON
    obj.Status = STATUS_TESTING
    if not hw_channel.funcs[obj.IFType][obj.TestType] then
        obj.Status = STATUS_COMPLETE
        self.hw_result[index] = RESULT_FAILED
        self.hw_description[index] = "IFType error or TestType error"
        return
    end
    local ret, description = hw_channel.funcs[obj.IFType][obj.TestType](obj)
    if ret == OK then
        obj.Status = STATUS_COMPLETE
        self.hw_result[index] = RESULT_SUCCEED
        self.hw_description[index] = description
        return
    end
    obj.Status = STATUS_COMPLETE
    self.hw_result[index] = RESULT_FAILED
    self.hw_description[index] = description
end

function manufacture_app:DftHwChannelStop(obj)
    local index = string.format("%d_%d_%d", obj.Id, obj.Slot, obj.DeviceNum)
    obj.Status = STATUS_UNSTART
    self.hw_result[index] = RESULT_NON
    self.hw_description[index] = ""
end

function manufacture_app:DftHwChannelGetResult(obj)
    local index = string.format("%d_%d_%d", obj.Id, obj.Slot, obj.DeviceNum)
    obj.Status = STATUS_COMPLETE
    return self.hw_result[index], self.hw_description[index]
end

return manufacture_app